﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using ZedGraph;

namespace WPFSerialAssistant
{
    public enum ReceiveMode
    {
        Character,  //字符显示
        Hex,        //十六进制
        Decimal,    //十进制
        Octal,      //八进制
        Binary      //二进制
    }

    public enum SendMode
    {
        Character,  //字符
        Hex         //十六进制
    }

    

    public partial class MainWindow : Window
    {
        #region Global

        // 发送的方式
        private SendMode sendMode = SendMode.Character;

        byte state = (byte)0;
        Int16 speed = 0;
        Int16 ch2_data = 0;
        Int16 ch3_data = 0;
        Int16 ch4_data = 0;
        //数值显示区
        Int16 A_Kp = 0;
        Int16 A_Ki = 0;
        Int16 I_Kp = 0;
        Int16 I_Ki = 0;
        Int16 S_Kp = 0;
        Int16 S_Ki = 0;

        Int16[] CMD_data = new Int16[12];
        string[] Tb_bak = new string[12];
        bool g_ChangeParameters = false;
        ulong TextFlag = 0;

        PointPairList list_ch1 = new PointPairList();
        PointPairList list_ch2 = new PointPairList();
        PointPairList list_ch3 = new PointPairList();
        PointPairList list_ch4 = new PointPairList();
        UInt32 speed_cnt = 0;
        UInt16 cmd_flag = 0;
        UInt16 hand_shake_flag = 0;
        bool speed_zed_flag = false;

        #endregion

        #region Event handler for menu items

        private void set_Button_Click(object sender, RoutedEventArgs e)
        {
            if (cmd_flag == 0)
            {
                // cmd_yButton.Content = "确认";
                if (!g_ChangeParameters)
                {
                    g_ChangeParameters = true;
                    f_TestBoxChangeEnable(true);
                    tB_SetColor(System.Windows.Media.Brushes.LightGreen);
                }
                else
                {
                    g_ChangeParameters = false;
                    f_TestBoxChangeEnable(false);
                    tB_SetColor(System.Windows.Media.Brushes.White);
                }
                cmd_flag = 1;
            }
        }

        private void cmd_yButton_Click(object sender, RoutedEventArgs e)
        {
            if (cmd_flag == 1)
            {
                // cmd_yButton.Content = "设置";
                CheckParaText();    //检测更改的参数位置，返回值为改变数据的

                if (GetTextFlag(0)) { sendcmd(2, CMD1_TB.Text); }
                if (GetTextFlag(1)) { sendcmd(3, CMD2_TB.Text); }
                if (GetTextFlag(2)) { sendcmd(4, CMD3_TB.Text); }
                if (GetTextFlag(3)) { sendcmd(5, CMD4_TB.Text); }
                if (GetTextFlag(4)) { sendcmd(6, CMD5_TB.Text); }
                if (GetTextFlag(5)) { sendcmd(7, CMD6_TB.Text); }
                if (GetTextFlag(6)) { sendcmd(8, CMD7_TB.Text); }
                if (GetTextFlag(7)) { sendcmd(9, CMD8_TB.Text); }
                if (GetTextFlag(8)) { sendcmd(10, CMD9_TB.Text); }
                if (GetTextFlag(9)) { sendcmd(11, CMD10_TB.Text); }
                if (GetTextFlag(10)) { sendcmd(12, CMD11_TB.Text); }
                if (GetTextFlag(11)) { sendcmd(13, CMD12_TB.Text); }
                g_ChangeParameters = false;
                f_TestBoxChangeEnable(false);

                tB_SetColor(System.Windows.Media.Brushes.White);
                cmd_flag = 0;
            }
            //if (cmd_flag == 1)
            //{
            //    sendcmd(2, CMD1_TB.Text);
            //    sendcmd(3, CMD2_TB.Text);
            //    sendcmd(4, CMD3_TB.Text); 
            //    sendcmd(5, CMD4_TB.Text);
            //    sendcmd(6, CMD5_TB.Text);
            //    sendcmd(7, CMD6_TB.Text);
            //    sendcmd(8, CMD7_TB.Text);
            //    sendcmd(9, CMD8_TB.Text);
            //    sendcmd(10, CMD9_TB.Text);
            //    sendcmd(11, CMD10_TB.Text);
            //    sendcmd(12, CMD11_TB.Text);
            //    sendcmd(13, CMD12_TB.Text);


            //    f_TestBoxChangeEnable(false);

            //    tB_SetColor(System.Windows.Media.Brushes.White);
            //    cmd_flag = 0;
            //}
        }

        private void saveConfigMenuItem_Click(object sender, RoutedEventArgs e)
        {
            SaveConfig();
            // 状态栏显示保存成功
            Information("配置信息保存成功。");
        }

        private void loadConfigMenuItem_Click(object sender, RoutedEventArgs e)
        {
            LoadConfig();
            // 状态栏显示加载成功
            Information("配置信息加载成功。");
        }

        private void exitMenuItem_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void serialSettingViewMenuItem_Click(object sender, RoutedEventArgs e)
        {
            bool state = serialSettingViewMenuItem.IsChecked;

            if (state == false)
            {
                serialPortConfigPanel.Visibility = Visibility.Visible;
            }
            else
            {
                serialPortConfigPanel.Visibility = Visibility.Collapsed;
                if (IsCompactViewMode())
                {
                    serialPortConfigPanel.Visibility = Visibility.Visible;
                    EnterCompactViewMode();
                }
            }

            serialSettingViewMenuItem.IsChecked = !state;
        }
       

        private void serialCommunicationSettingViewMenuItem_Click(object sender, RoutedEventArgs e)
        {
            bool state = serialCommunicationSettingViewMenuItem.IsChecked;

            if (state == false)
            {
                serialCommunicationConfigPanel.Visibility = Visibility.Visible;
            }
            else
            {
                serialCommunicationConfigPanel.Visibility = Visibility.Collapsed;

                if (IsCompactViewMode())
                {
                    serialCommunicationConfigPanel.Visibility = Visibility.Visible;
                    EnterCompactViewMode();
                }
            }

            serialCommunicationSettingViewMenuItem.IsChecked = !state;
        }

        private void compactViewMenuItem_Click(object sender, RoutedEventArgs e)
        {
            if (IsCompactViewMode())
            {
                RestoreViewMode();
            }
            else
            {
                EnterCompactViewMode();
            }
        }

        private void aboutMenuItem_Click(object sender, RoutedEventArgs e)
        {
            WPFSerialAssistant.About about = new About();
            about.ShowDialog();            
        }

        private void helpMenuItem_Click(object sender, RoutedEventArgs e)
        {

        }
        #endregion

        #region Event handler for buttons and so on.
        private void openClosePortButton_Click(object sender, RoutedEventArgs e)
        {
            if (serialPort.IsOpen)
            {
                if (ClosePort())
                {
                    openClosePortButton.Content = "打开";
                }
            }
            else
            {
                if (OpenPort())
                {
                    openClosePortButton.Content = "关闭";
                }
            }
            CleraData();
        }
        
        private void findPortButton_Click(object sender, RoutedEventArgs e)
        {
            // FindPorts();
            if(hand_shake_flag == 0)
            {
                hand_shake_flag = 1;
                updateTimer.Start();
                connectButton.Content = "断开";
                sendcmd(0, "1");
            }
            else
            {
                hand_shake_flag = 0;
                updateTimer.Stop();
                connectButton.Content = "连接";
                sendcmd(0, "0");
               
            }          
        }

        private void ClearButton_Click(object sender, RoutedEventArgs e)
        {
            CleraData();
        }

        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            SaveData(GetSaveDataPath());
        }

        private void CleraData()
        {
            speed_cnt = 0;
            if (list_ch1 != null)
            {
                int len = list_ch1.Count;
                list_ch1.RemoveRange(0, len);
            }
            if (list_ch2 != null)
            {
                int len = list_ch2.Count;
                list_ch2.RemoveRange(0, len);
            }
            if (list_ch3 != null)
            {
                int len = list_ch3.Count;
                list_ch3.RemoveRange(0, len);
            }
            if (list_ch4 != null)
            {
                int len = list_ch4.Count;
                list_ch4.RemoveRange(0, len);
            }
        }


        private void startButton_Click(object sender, RoutedEventArgs e)
        {
            sendcmd(1, "1");
        }

        private void stopButton_Click(object sender, RoutedEventArgs e)
        {
            sendcmd(1, "0");
        }
       
        #endregion

        #region Event handler for timers
        private void ClockTimer_Tick(object sender, EventArgs e)
        {
            UpdateTimeDate();
        }

        private void updateTimer_Tick(object sender, EventArgs e)
        {
            if (speed_zed_flag)
            {
                speed_zed_flag = false;
                this.Dispatcher.Invoke(new Action(() =>
                {
                    // 根据显示模式显示接收到的字节.
                    switch (state)
                    {
                        case 0:
                            D_stateTB.Background = System.Windows.Media.Brushes.White;
                            D_stateTB.Text = "Stop";
                            break;
                        case 1:
                            D_stateTB.Background = System.Windows.Media.Brushes.Green;
                            D_stateTB.Text = "Start";
                            break;
                        case 3:
                            D_stateTB.Background = System.Windows.Media.Brushes.Green;
                            D_stateTB.Text = "Run";
                            break;
                        case 4:
                            D_stateTB.Background = System.Windows.Media.Brushes.Red;
                            D_stateTB.Text = "Error";
                            break;
                        default: break;
                    }

                    D_A_Kp_TB.Text = Convert.ToString(A_Kp);
                    D_A_Ki_TB.Text = Convert.ToString(A_Ki);
                    D_I_Kp_TB.Text = Convert.ToString(I_Kp);
                    D_I_Ki_TB.Text = Convert.ToString(I_Ki);
                    D_S_Kp_TB.Text = Convert.ToString(S_Kp);
                    D_S_Ki_TB.Text = Convert.ToString(S_Ki);

                    if (!g_ChangeParameters)
                    {
                        Tb_bak[0] = Convert.ToString(CMD_data[0]);
                        CMD1_TB.Text = Tb_bak[0];
                        Tb_bak[1] = Convert.ToString(CMD_data[1]);
                        CMD2_TB.Text = Tb_bak[1];
                        Tb_bak[2] = Convert.ToString(CMD_data[2]);
                        CMD3_TB.Text = Tb_bak[2];
                        Tb_bak[3] = Convert.ToString(CMD_data[3]);
                        CMD4_TB.Text = Tb_bak[3];
                        Tb_bak[4] = Convert.ToString(CMD_data[4]);
                        CMD5_TB.Text = Tb_bak[4];
                        Tb_bak[5] = Convert.ToString(CMD_data[5]);
                        CMD6_TB.Text = Tb_bak[5];
                        Tb_bak[6] = Convert.ToString(CMD_data[6]);
                        CMD7_TB.Text = Tb_bak[6];
                        Tb_bak[7] = Convert.ToString(CMD_data[7]);
                        CMD8_TB.Text = Tb_bak[7];
                        Tb_bak[8] = Convert.ToString(CMD_data[8]);
                        CMD9_TB.Text = Tb_bak[8];
                        Tb_bak[9] = Convert.ToString(CMD_data[9]);
                        CMD10_TB.Text = Tb_bak[9];
                        Tb_bak[10] = Convert.ToString(CMD_data[10]);
                        CMD11_TB.Text = Tb_bak[10];
                        Tb_bak[11] = Convert.ToString(CMD_data[11]);
                        CMD12_TB.Text = Tb_bak[11];
                    }

                    RefreshInterface();

                    dataRecvStatusBarItem.Visibility = Visibility.Collapsed;
                }));

            }
        }

        /// <summary>
        /// 窗口关闭前拦截
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // 释放没有关闭的端口资源
            if (serialPort.IsOpen)
            {
                ClosePort();
                speed_cnt = 0;
            }

            // 提示是否需要保存配置到文件中
            if (MessageBox.Show("是否在退出前保存软件配置？", "小贴士", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes)
            {
                SaveConfig();
            }
        }

        /// <summary>
        /// 捕获窗口按键。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            // Ctrl+S保存数据
            if (e.Key == Key.S && e.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
            {
                SaveData(GetSaveDataPath());
            }

            // Ctrl+Enter 进入/退出简洁视图模式
            if (e.Key == Key.Enter && e.KeyboardDevice.IsKeyDown(Key.LeftCtrl))
            {
                if (IsCompactViewMode())
                {
                    RestoreViewMode();
                }
                else
                {
                    EnterCompactViewMode();
                }
            }
        }

        #endregion

        #region EventHandler for serialPort
        
        // 数据接收缓冲区
        private List<byte> receiveBuffer = new List<byte>();

        // 一个阈值，当接收的字节数大于这么多字节数之后，就将当前的buffer内容交由数据处理的线程
        // 分析。这里存在一个问题，假如最后一次传输之后，缓冲区并没有达到阈值字节数，那么可能就
        // 没法启动数据处理的线程将最后一次传输的数据处理了。这里应当设定某种策略来保证数据能够
        // 在尽可能短的时间内得到处理。
        private const int THRESH_VALUE = 64;

        /// <summary>
        /// 更新：采用一个缓冲区，当有数据到达时，把字节读取出来暂存到缓冲区中，缓冲区到达定值
        /// 时，在显示区显示数据即可。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            System.IO.Ports.SerialPort sp = sender as System.IO.Ports.SerialPort;

            if (sp != null)
            {
                // 临时缓冲区将保存串口缓冲区的所有数据
                int bytesToRead = sp.BytesToRead;
                byte[] tempBuffer = new byte[bytesToRead];
                List<byte> readBuffer = new List<byte>();

                // 将缓冲区所有字节读取出来
                sp.Read(tempBuffer, 0, bytesToRead);

                // 暂存缓冲区字节到全局缓冲区中等待处理
                receiveBuffer.AddRange(tempBuffer);


                while (receiveBuffer.Count >= 1)
                {
                    if (receiveBuffer[0] == 0xa1)
                    {
                        if (receiveBuffer.Count < THRESH_VALUE)
                            break;
                        else
                        {
                            readBuffer = receiveBuffer.GetRange(0, THRESH_VALUE);
                            // readBuffer = new byte[THRESH_VALUE];
                            //得到完整的数据，复制到readBuffer中    
                            // receiveBuffer.CopyTo(0, readBuffer, 0, THRESH_VALUE);
                            //从缓冲区中清除
                            int rec_len = receiveBuffer.Count;
                            receiveBuffer.RemoveRange(0, THRESH_VALUE);
                            // 进行数据处理，采用新的线程进行处理。
                            if (readBuffer[63] != 0x56)
                            {
                                continue;
                            }
                            else
                            {
                                Thread dataHandler = new Thread(new ParameterizedThreadStart(ReceivedDataHandler));
                                dataHandler.Start(readBuffer);
                                //readBuffer.RemoveRange(0, THRESH_VALUE);

                                // TO-DO：
                                // 处理数据，比如解析指令等等
                            }
                        }
                    }
                    else
                    {
                        int len = receiveBuffer.Count;
                        receiveBuffer.RemoveRange(0, len);
                    }
                }

                // 启动定时器，防止因为一直没有到达缓冲区字节阈值，而导致接收到的数据一直留存在缓冲区中无法处理。
                StartCheckTimer();

                this.Dispatcher.Invoke(new Action(() =>
                {
                    dataRecvStatusBarItem.Visibility = Visibility.Visible;
                }));
            }
        }

        #endregion

        #region 数据处理

        private void CheckTimer_Tick(object sender, EventArgs e)
        {
            // 触发了就把定时器关掉，防止重复触发。
             StopCheckTimer();

            // 只有没有到达阈值的情况下才会强制其启动新的线程处理缓冲区数据。
            if (receiveBuffer.Count < THRESH_VALUE)
            {
                ////recvDataRichTextBox.AppendText("Timeout!\n");
                //// 进行数据处理，采用新的线程进行处理。
                Thread dataHandler = new Thread(new ParameterizedThreadStart(ReceivedDataHandler));
                dataHandler.Start(receiveBuffer);

                // TO-DO：
                // 处理数据，比如解析指令等等

            }          
        }
        //a1 命令字 数据（2字节） 78 56 (一共8字节)
        private void sendcmd(int p, string p_2) //03e8
        {
            byte[] data = new byte[8];
            if (string.IsNullOrWhiteSpace(p_2) == false)
            {
                UInt16 temdata = (UInt16)Convert.ToInt16(p_2);
                data[0] = 0xa1;
                data[1] = (byte)(p);
                data[2] = (byte)(temdata >> 0);//e8
                data[3] = (byte)(temdata >> 8);//03
                data[6] = 0x34;
                data[7] = 0x12;
                SerialPortWrite(data);
                Thread.Sleep(100);
            }
            else
            {
                Alert("输入不能为空！");
            }
        }

   

        ////刷新界面
        private void RefreshInterface()
        {
            zedGraph_speed.GraphPane.CurveList.Clear();//这句话的意思是把曲线清零
            if(CH1_CB.IsChecked == true)
            {
                zedGraph_speed.GraphPane.AddCurve("Speed", list_ch1, System.Drawing.Color.Red, SymbolType.None);//绘制图表
            }
            if (CH2_CB.IsChecked == true)
            {
                zedGraph_speed.GraphPane.AddCurve("CH2", list_ch2, System.Drawing.Color.Blue, SymbolType.None);//绘制图表
            }
            if (CH3_CB.IsChecked == true)
            {
                zedGraph_speed.GraphPane.AddCurve("CH3", list_ch3, System.Drawing.Color.Green, SymbolType.None);//绘制图表
            }
            if (CH4_CB.IsChecked == true)
            {
                zedGraph_speed.GraphPane.AddCurve("CH4", list_ch4, System.Drawing.Color.Yellow, SymbolType.None);//绘制图表
            }
            zedGraph_speed.AxisChange();//刷新界面
            zedGraph_speed.Refresh();
        }
        private void ReceivedDataHandler(object obj)
        {
            List<byte> recvBuffer = new List<byte>();
            recvBuffer.AddRange((List<byte>)obj);
           
            if (recvBuffer.Count == 0)
            {
                return;
            }

            // TO-DO：
            // 处理数据，比如解析指令等等

            UInt32 sum = 0;
            byte i = 0;
            for ( i = 0; i < 61; i++)
            {
                sum = sum + recvBuffer[i];
            }
                       
            if ((byte)(sum) == recvBuffer[61])
            {
                state = recvBuffer[1];
                //曲线2-13 6条 预留2条
                speed =    (Int16)Convert.ToUInt16(recvBuffer[2] | recvBuffer[3] << 8);
                ch2_data = (Int16)Convert.ToUInt16(recvBuffer[4] | recvBuffer[5] << 8);
                ch3_data = (Int16)Convert.ToUInt16(recvBuffer[6] | recvBuffer[7] << 8);
                ch4_data = (Int16)Convert.ToUInt16(recvBuffer[8] | recvBuffer[9] << 8);
                //数值显示14-25
                A_Kp = (Int16)Convert.ToUInt16(recvBuffer[14] | recvBuffer[15] << 8);
                A_Ki = (Int16)Convert.ToUInt16(recvBuffer[16] | recvBuffer[17] << 8);
                I_Kp = (Int16)Convert.ToUInt16(recvBuffer[18] | recvBuffer[19] << 8);
                I_Ki = (Int16)Convert.ToUInt16(recvBuffer[20] | recvBuffer[21] << 8);
                S_Kp = (Int16)Convert.ToUInt16(recvBuffer[22] | recvBuffer[23] << 8);
                S_Ki = (Int16)Convert.ToUInt16(recvBuffer[24] | recvBuffer[25] << 8);
                //命令 31-54
                int cmd_i = 0;
                for(cmd_i = 0; cmd_i < CMD_data.Length; cmd_i++)
                {
                    CMD_data[cmd_i] = (Int16)Convert.ToUInt16(recvBuffer[31+2*cmd_i] | recvBuffer[32+2*cmd_i] << 8);
                }

                if (speed_cnt <= 4294967295)
                {
                    speed_cnt++;
                }
                else
                {
                    CleraData();
                }
                list_ch1.Add(speed_cnt, speed);
                list_ch2.Add(speed_cnt, ch2_data);
                list_ch3.Add(speed_cnt, ch3_data);
                list_ch4.Add(speed_cnt, ch4_data);
                speed_zed_flag = true;
            }
            else
            {
                return;
            }

        }
        #endregion

       

        private void f_TestBoxChangeEnable(Boolean flag)
        {
            CMD1_TB.IsEnabled = flag;
            CMD2_TB.IsEnabled = flag;
            CMD3_TB.IsEnabled = flag;
            CMD4_TB.IsEnabled = flag;
            CMD5_TB.IsEnabled = flag;
            CMD6_TB.IsEnabled = flag;
            CMD7_TB.IsEnabled = flag;
            CMD8_TB.IsEnabled = flag;
            CMD9_TB.IsEnabled = flag;
            CMD10_TB.IsEnabled = flag;
            CMD11_TB.IsEnabled = flag;
            CMD12_TB.IsEnabled = flag;
        }

        private void tB_SetColor(System.Windows.Media.Brush m_color)
        {
            CMD1_TB.Background = m_color;
            CMD2_TB.Background = m_color;
            CMD3_TB.Background = m_color;
            CMD4_TB.Background = m_color;
            CMD5_TB.Background = m_color;
            CMD6_TB.Background = m_color;
            CMD7_TB.Background = m_color;
            CMD8_TB.Background = m_color;
            CMD9_TB.Background = m_color;
            CMD10_TB.Background = m_color;
            CMD11_TB.Background = m_color;
            CMD12_TB.Background = m_color;
        }


        private void CheckParaText()
        {
            if (Tb_bak[0] != CMD1_TB.Text) { SetTextChange(0, true); } else SetTextChange(0, false);
            if (Tb_bak[1] != CMD2_TB.Text) { SetTextChange(1, true); } else SetTextChange(1, false);
            if (Tb_bak[2] != CMD3_TB.Text) { SetTextChange(2, true); } else SetTextChange(2, false);
            if (Tb_bak[3] != CMD4_TB.Text) { SetTextChange(3, true); } else SetTextChange(3, false);
            if (Tb_bak[4] != CMD5_TB.Text) { SetTextChange(4, true); } else SetTextChange(4, false);
            if (Tb_bak[5] != CMD6_TB.Text) { SetTextChange(5, true); } else SetTextChange(5, false);
            if (Tb_bak[6] != CMD7_TB.Text) { SetTextChange(6, true); } else SetTextChange(6, false);
            if (Tb_bak[7] != CMD8_TB.Text) { SetTextChange(7, true); } else SetTextChange(7, false);
            if (Tb_bak[8] != CMD9_TB.Text) { SetTextChange(8, true); } else SetTextChange(8, false);
            if (Tb_bak[9] != CMD10_TB.Text) { SetTextChange(9, true); } else SetTextChange(9, false);
            if (Tb_bak[10] != CMD11_TB.Text) { SetTextChange(10, true); } else SetTextChange(10, false);
            if (Tb_bak[11] != CMD12_TB.Text) { SetTextChange(11, true); } else SetTextChange(11, false);
        }


        private void SetTextChange(int DataBit, bool flag)      //设置TEXT位置，是否需要更改，如需更改，置位TextFlag
        {
            if (flag)
            {
                TextFlag |= (1ul << DataBit);
            }
            else
            {
                TextFlag &= ~(1ul << DataBit);
            }
        }

        private bool GetTextFlag(int DataBit)
        {
            ulong temp = (1ul << DataBit);
            if ((TextFlag & temp) > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }


}


